using System;
using FireStarter.Base;
using Firestarter.DB.Schema.BO;
using Firestarter.DB.Schema.Logic;
using FireStarterModeller.BO;
using FireStarterModeller.Logic;
using Habanero.BO;
using Habanero.BO.ClassDefinition;
using Habanero.BO.Loaders;
using NUnit.Framework;
 
using Rhino.Mocks;

namespace Firestarter.DB_DomainModel.Logic.Test
{

    //TODO Mark 16 Mar 2009: Look at Single Table Inheritance import prop
    [TestFixture]
    public class TestDBToDMPropertyImporter
    {
        [TestFixtureSetUp]
        public virtual void TestFixtureSetup()
        {
            ClassDef.ClassDefs.Clear();
            ClassDef.ClassDefs.Add(new XmlClassDefsLoader(BOBroker.GetClassDefsXml(), new DtdLoader()).LoadClassDefs());
        }

        // ReSharper disable RedundantNameQualifier
        [SetUp]
        public virtual void SetupTest()
        {
            GC.Collect();
            GC.WaitForPendingFinalizers();
            BORegistry.DataAccessor = new DataAccessorInMemory();
        }

        private static DBToDMPropertyImporter CreateDBToDMPropertyImporter()
        {
            IDBToDMPropertyUpdater dbToDMPropertyUpdater = MockRepository.GenerateStub<IDBToDMPropertyUpdater>();
            return new DBToDMPropertyImporter(dbToDMPropertyUpdater);
        }

        [Test]
        public void Test_Construct()
        {
            //---------------Set up test pack-------------------
            IDBToDMPropertyUpdater dbToDMPropertyUpdater = MockRepository.GenerateStub<IDBToDMPropertyUpdater>();
            //---------------Assert Precondition----------------
            //---------------Execute Test ----------------------
            DBToDMPropertyImporter dbToDMPropertyImporter = new DBToDMPropertyImporter(dbToDMPropertyUpdater);
            //---------------Test Result -----------------------
            Assert.IsNotNull(dbToDMPropertyImporter);
            Assert.AreSame(dbToDMPropertyUpdater, dbToDMPropertyImporter.DBToDMPropertyUpdater);
        }

        [Test]
        public void Test_Construct_WhenNullDBToDMPropertyUpdater_ShouldThrowError()
        {
            //---------------Set up test pack-------------------
            //---------------Assert Precondition----------------
            //---------------Execute Test ----------------------
            try
            {
                new DBToDMPropertyImporter(null);
                //---------------Test Result -----------------------
                Assert.Fail("Expected to throw an ArgumentNullException");
            }
            catch (ArgumentNullException ex)
            {
                Assert.AreEqual("dbToDMPropertyUpdater", ex.ParamName);
            }
        }

        [Test]
        public void Test_Import_WhenNullDMClass_ShouldThrowError()
        {
            //---------------Set up test pack-------------------
            DBToDMPropertyImporter dbToDMPropertyImporter = CreateDBToDMPropertyImporter();
            //---------------Assert Precondition----------------
            //---------------Execute Test ----------------------
            try
            {
                dbToDMPropertyImporter.Import(null, GetDBColumn());
                //---------------Test Result -----------------------
                Assert.Fail("Expected to throw an ArgumentNullException");
            }
            catch (ArgumentNullException ex)
            {
                Assert.AreEqual("dmClass", ex.ParamName);
            }
        }


        [Test]
        public void Test_Import_WhenColumnIsNull_ShouldThrowError()
        {
            //---------------Set up test pack-------------------
            DBToDMPropertyImporter dbToDMPropertyImporter = CreateDBToDMPropertyImporter();
            //---------------Assert Precondition----------------

            //---------------Execute Test ----------------------
            try
            {
                dbToDMPropertyImporter.Import(new DMClass(), null);
                Assert.Fail("expected ArgumentNullException");
            }
                //---------------Test Result -----------------------
            catch (ArgumentNullException ex)
            {
                StringAssert.Contains("Value cannot be null", ex.Message);
                StringAssert.Contains("dbColumn", ex.ParamName);
            }
        }

        [Test]
        public void Test_Import_WhenPropertyDoesNotExist_ShouldAddNewDMProperty()
        {
            //---------------Set up test pack-------------------
            IDBColumn dbColumn = GetDBColumn("FieldName");
            DMClass dmClass = new DMClass();
            DMProperty otherProperty = new DMProperty();
            dmClass.Properties.Add(otherProperty);
            DBToDMPropertyImporter dbToDMPropertyImporter = CreateDBToDMPropertyImporter(); 
            //---------------Assert Precondition----------------
            Assert.AreEqual(1, dmClass.Properties.Count);
            //---------------Execute Test ----------------------
            DMProperty returnedDMProperty = dbToDMPropertyImporter.Import(dmClass, dbColumn);
            //---------------Test Result -----------------------
            Assert.AreEqual(2, dmClass.Properties.Count);
            Assert.AreNotSame(otherProperty, returnedDMProperty);
            Assert.IsTrue(dmClass.Properties.Contains(returnedDMProperty));
            Assert.AreEqual(dbColumn.ColumnName, returnedDMProperty.MappedDBColumnName);
            Assert.AreEqual(dbColumn.ColumnID, returnedDMProperty.MappedDBColumnID);
        }


        [Test]
        public void Test_Import_WhenPropertyDoesNotExist_ShouldCallDBToDMPropertyUpdater()
        {
            //---------------Set up test pack-------------------
            IDBColumn dbColumn = GetDBColumn("FieldName");
            DMClass dmClass = new DMClass();
            DBToDMPropertyImporter dbToDMPropertyImporter = CreateDBToDMPropertyImporter();
            //---------------Assert Precondition----------------
            Assert.IsTrue(dbColumn.Nullable.GetValueOrDefault());
            //---------------Execute Test ----------------------
            DMProperty boProp = dbToDMPropertyImporter.Import(dmClass, dbColumn);
            //---------------Test Result -----------------------
            dbToDMPropertyImporter.DBToDMPropertyUpdater.AssertWasCalled(updater => updater.UpdateValues(dbColumn, boProp));
        }

        [Test]
        public void Test_Import_WhenPropertyExists_WithOnlyMappedDBColumnSet_ShouldCallDBToDMPropertyUpdater()
        {
            //---------------Set up test pack-------------------
            IDBColumn dbColumn = GetDBColumn("FieldName");
            DMClass dmClass = new DMClass();
            DMProperty existingDMProperty = new DMProperty { MappedDBColumn = dbColumn };
            dmClass.Properties.Add(existingDMProperty);
            DBToDMPropertyImporter dbToDMPropertyImporter = CreateDBToDMPropertyImporter();
            //---------------Assert Precondition----------------
            Assert.IsTrue(dbColumn.Nullable.GetValueOrDefault());
            //---------------Execute Test ----------------------
            DMProperty returnedDMProperty = dbToDMPropertyImporter.Import(dmClass, dbColumn);
            //---------------Test Result -----------------------
            Assert.AreSame(existingDMProperty, returnedDMProperty);
            dbToDMPropertyImporter.DBToDMPropertyUpdater.AssertWasCalled(updater => updater.UpdateValues(dbColumn, existingDMProperty));
        }

        [Test]
        public void Test_Import_WhenPropertyExists_WithOnlyMappedDBColumnNameSet_ShouldCallDBToDMPropertyUpdater()
        {
            //---------------Set up test pack-------------------
            IDBColumn dbColumn = GetDBColumn("FieldName");
            DMClass dmClass = new DMClass();
            DMProperty existingDMProperty = new DMProperty { MappedDBColumnName = dbColumn.ColumnName };
            dmClass.Properties.Add(existingDMProperty);
            DBToDMPropertyImporter dbToDMPropertyImporter = CreateDBToDMPropertyImporter();
            //---------------Assert Precondition----------------
            Assert.IsTrue(dbColumn.Nullable.GetValueOrDefault());
            //---------------Execute Test ----------------------
            DMProperty returnedDMProperty = dbToDMPropertyImporter.Import(dmClass, dbColumn);
            //---------------Test Result -----------------------
            Assert.AreSame(existingDMProperty, returnedDMProperty);
            dbToDMPropertyImporter.DBToDMPropertyUpdater.AssertWasCalled(updater => updater.UpdateValues(dbColumn, existingDMProperty));
        }

        [Test]
        public void Test_Import_WhenPropertyExists_WithOnlyMappedDBColumnSet_ShouldUpdateMappedDBColumnName()
        {
            //---------------Set up test pack-------------------
            IDBColumn dbColumn = GetDBColumn("FieldName");
            DMClass dmClass = new DMClass();
            DMProperty existingDMProperty = new DMProperty { MappedDBColumnID = dbColumn.ColumnID };
            dmClass.Properties.Add(existingDMProperty);
            DBToDMPropertyImporter dbToDMPropertyImporter = CreateDBToDMPropertyImporter();
            //---------------Assert Precondition----------------
            Assert.AreEqual(dbColumn.ColumnID, existingDMProperty.MappedDBColumnID);
            Assert.IsNull(existingDMProperty.MappedDBColumnName);
            //---------------Execute Test ----------------------
            DMProperty returnedDMProperty = dbToDMPropertyImporter.Import(dmClass, dbColumn);
            //---------------Test Result -----------------------
            Assert.AreSame(existingDMProperty, returnedDMProperty);
            Assert.AreEqual(dbColumn.ColumnID, existingDMProperty.MappedDBColumnID);
            Assert.AreEqual(dbColumn.ColumnName, existingDMProperty.MappedDBColumnName);
        }

        [Test]
        public void Test_Import_WhenPropertyExists_WithOnlyMappedDBColumnNameSet_ShouldUpdateMappedDBColumnID()
        {
            //---------------Set up test pack-------------------
            IDBColumn dbColumn = GetDBColumn("FieldName");
            DMClass dmClass = new DMClass();
            DMProperty existingDMProperty = new DMProperty { MappedDBColumnName = dbColumn.ColumnName };
            dmClass.Properties.Add(existingDMProperty);
            DBToDMPropertyImporter dbToDMPropertyImporter = CreateDBToDMPropertyImporter();
            //---------------Assert Precondition----------------
            Assert.AreEqual(dbColumn.ColumnName, existingDMProperty.MappedDBColumnName);
            Assert.IsNull(existingDMProperty.MappedDBColumnID);
            //---------------Execute Test ----------------------
            DMProperty returnedDMProperty = dbToDMPropertyImporter.Import(dmClass, dbColumn);
            //---------------Test Result -----------------------
            Assert.AreSame(existingDMProperty, returnedDMProperty);
            Assert.AreEqual(dbColumn.ColumnName, existingDMProperty.MappedDBColumnName);
            Assert.AreEqual(dbColumn.ColumnID, existingDMProperty.MappedDBColumnID);
        }

        /// <summary>
        /// ReverseEngineer From DB : When PropName same as ClassName
        /// - Often in DB tables like Status have a column Status.
        /// - This is illegal for code.
        /// - Convert Property 'Status' to StatusName in reverseEngineering if string
        /// </summary>
        [Test]
        public void Test_Import_WhenPropertyDoesNotExist_WhenColumnNameEQClassName_WhenString_ShouldAddProperty_WithPropNameEqColumnNamePlusName_FixBug828()
        {
            //---------------Set up test pack-------------------
            const string columnName = "Field";
            IDBColumn dbColumn = GetDBColumn(columnName);
            dbColumn.DataType = DBDataTypes.VARCHAR;
            DMClass dmClass = new DMClass {ClassNameBO = columnName};
            DBToDMPropertyImporter dbToDMPropertyImporter = GetDBToDMPropertyImporter();
            //---------------Assert Precondition----------------
            Assert.AreEqual(0, dmClass.Properties.Count);
            Assert.AreEqual(dbColumn.ColumnName, dmClass.ClassNameBO);
            Assert.AreEqual(DBDataTypes.VARCHAR,  dbColumn.DataType);
            //---------------Execute Test ----------------------
            DMProperty returnedDMProperty = dbToDMPropertyImporter.Import(dmClass, dbColumn);
            //---------------Test Result -----------------------
            Assert.AreEqual("System.String", returnedDMProperty.PropertyTypeString);
            Assert.IsTrue(dmClass.Properties.Contains(returnedDMProperty));
            Assert.AreSame(dbColumn, returnedDMProperty.MappedDBColumn);

            Assert.AreEqual(columnName + "Name",  returnedDMProperty.PropertyName );
        }

        [Test]
        public void Test_Import_WhenPropertyDoesNotExist_WhenColumnNameEQClassName_WhenNotString_ShouldAddPropertyAsPerNormal_FixBug828()
        {
            //---------------Set up test pack-------------------
            const string columnName = "Field";
            IDBColumn dbColumn = GetDBColumn(columnName);
            dbColumn.DataType = DBDataTypes.SINGLE;
            DMClass dmClass = new DMClass { ClassNameBO = columnName };
            DBToDMPropertyImporter dbToDMPropertyImporter = GetDBToDMPropertyImporter();
            //---------------Assert Precondition----------------
            Assert.AreEqual(0, dmClass.Properties.Count);
            Assert.AreEqual(dbColumn.ColumnName, dmClass.ClassNameBO);
            Assert.AreEqual(DBDataTypes.SINGLE, dbColumn.DataType);
            //---------------Execute Test ----------------------
            DMProperty returnedDMProperty = dbToDMPropertyImporter.Import(dmClass, dbColumn);
            //---------------Test Result -----------------------
            Assert.AreNotEqual("System.String", returnedDMProperty.PropertyTypeString);
            Assert.IsTrue(dmClass.Properties.Contains(returnedDMProperty));
            Assert.AreSame(dbColumn, returnedDMProperty.MappedDBColumn);

            Assert.AreEqual(columnName, returnedDMProperty.PropertyName);
        }

        protected virtual IDBColumn GetDBColumn()
        {
            return new DBColumn();
        }

        protected virtual IDBColumn GetDBColumn(string columnName)
        {
            return new DBColumn(columnName);
        }
        protected DBToDMPropertyImporter GetDBToDMPropertyImporter()
        {
            IDBToDMPropertyUpdater dbToDMPropertyUpdater = new DBToDMPropertyUpdater(new DBTypeToDMPropertyTypeMapper(new PropertyTypeStorer(new SolutionCreator().CreateSolution())));
            return new DBToDMPropertyImporter(dbToDMPropertyUpdater);
        }
    }

    [TestFixture]
    public class TestDbtoDMPropertyImporter_DBViewColumn : TestDBToDMPropertyImporter
    {
        protected override IDBColumn GetDBColumn()
        {
            return new DBViewColumn();
        }

        protected override IDBColumn GetDBColumn(string columnName)
        {
            return new DBViewColumn(columnName);
        }
    }
}